home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Collections: MegaDisc
/
MegaDisc 07 (1988)(MegaDisc Digital Publishing)(AU)[WB].zip
/
MegaDisc 07 (1988)(MegaDisc Digital Publishing)(AU)[WB].adf
/
ARTICLES
/
Gfx_Prog
< prev
next >
Wrap
Text File
|
2000-04-10
|
31KB
|
602 lines
Programming Amiga Graphics
Overview
This article describes the graphics capabilities of the Amiga, both in
terms of the data structures and ROM Kernel routines which allow the
programmer to control the Amiga's display, and the custom graphics
circuitry which enables the system to create the displays requested by
the programmer. Although it is intended to give a prospective
programmer sufficient information (along with the appropriate Reference
Manuals) to tackle most facets of Amiga graphics programming, this
article concentrates on providing a conceptual understanding of the
interrelationships between various components of the graphics system. It
can therefore be read be profitably read by any Amiga user who wishes to
gain an insight into the workings of the Amiga's remarkable graphics.
Amiga graphics may be approached on three levels by the programmer, which
I shall call -
1. Intuition
2. Kernel
3. Copper
These names are purely of my choice, and may conflict with those chosen
by others for the same purpose.
Intuition is the name of the Amiga's user interface (which
includes screens,windows,menus etc), but I also use it to refer to a set
of data structures and routines which are designed to give the programmer
the easiest possible access to graphics while remaining within the
confines of the Intuition user interface. Intuition is designed to allow
the user to make effective use of the Amiga's multitasking capabilities,
so programs intending to co-operate with Intuition should restrict
themselves to Intuition-level graphics where possible. Although facilities
available at the Kernel and Copper levels may be used by such programs,
care must be taken that the Intuition interface is not destroyed by their
use.
Kernel is shorthand for "ROM Kernel", which is commonly used to
describe the routines in ROM which form the major part of the Amiga's
operating system. While some of the graphics routines in ROM support
Intuition-level graphics, the majority allow the user to control the
Amiga's graphics with more flexibility and provide the programmer with
much more power. The use of some Kernel-level features precludes the
existence (or at least availability to the user) of the Intuition
interface, hence programs which use these facilities are often referred
to as "nasty" or as "taking over the machine".
Kernel graphics allows the programmer to exercise very close control over
the appearance of the Amiga's display, providing easy access to the
capabilities of the graphics hardware; it generally provides the optimum
environment as regards simplicity, flexibility and speed of operation.
Copper is short for "Coprocessor", a simple microprocessor contained
in one of the custom chips which directly controls the Amiga's video
display using a list of simple instructions which form a rudimentary
"program". While this list is normally created and maintained by Kernel
routines, it is possible for the programmer to create a Copper list
directly - i.e. Copper-level programming. While more laborious and
complex than Kernel-level programming, Copper programming provides the
maximum possible control over the Amiga's graphics, making possible
effects and animations unobtainable with Kernel programming.
The Kernel provides a mechanism for safely integrating Copper programs
into Kernel or even Intuition displays; this will be discussed in
conjunction with direct Copper control.
Note that the distinctions between these "levels" of programming are
somewhat blurred - the Intuition interface is built directly on the
Kernel routines and structures, and the Kernel relies, ultimately, on
the Copper to create your display.
The material from here on becomes more technically detailed. If you are
not familiar with such concepts as "data structures","routines" and
"pointers" please read Appendix A. The concepts explained there are
fundamental to the rest of this article.
Amiga Graphics Fundamentals
To draw and display graphics on the Amiga, the first requirement is
that you have a block of screen memory of the appropriate size
to work with. This memory block may be anywhere in the lowest-addressed
512 Kbytes of the Amiga's memory. Since the operating system maintains
internally a list of memory blocks which are free (i.e. not in use by
any other tasks running in the system), you should call one of the
memory-allocations routines to have the system allocate to you a block
of screen memory of the size you require. The routine
AllocRaster is designed specifically for allocating screen
memory, and returns the address of a block of memory of the size you
specify which you may safely use.
This screen memory is organised, from the programmer's point of view,
as a rectangular "bit plane", which has a width and height (in pixels)
specified by you. The first bit of the memory block is the upper left
corner pixel of the rectangle, while the last bit is the lower right
corner pixel; the leftmost pixel of each line and the rightmost of the
previous line correspond to adjacent bits in your memory block. It is
apparent that a given memory block may serve as a bit plane of any width
and height, provided the overall area remains the same.
If you wish to display more than two colours in your graphics region
you must allocate memory for more than one bit plane. Imagine that these
planes are directly overlaid on each other; the colour of each pixel is
determined by the combination of the corresponding bits in each
bit-plane. Each plane you use doubles the number of available colours,
up to a limit of 6 planes (see Graphics Modes). Since these planes are
conventionally numbered from zero upward, each pixel in the region can
have a "colour number" ranging from 0 to an upper limit determined by
the number of bit planes used. The means by which the colour number of
a pixel determines the displayed colour is explained in Graphics Modes
and Colours.
The combination of a number of bit planes of given width and height is
known as a "bit map", and is described by a data structure called a
"BitMap". This structure specifies the number of bytes in each row
(horizontal line) of the bit map (there are eight bits or pixels in one
byte), the height of the bit map in screen lines and the number of
planes used (or "depth"). It also contains a pointer to each bit plane.
Note that a bit map has maximum dimensions of 1024 by 1024 pixels.
Rendering versus Displaying
The facilities provided by any computer's graphics system may be, in
general, divided into "Rendering" and "Displaying". Rendering into a
bit map is the act of modifying its pixels, either singly or in groups.
It is important to understand that on the Amiga there is no requirement
that the bit map you render into be visible on the screen, or that a
bit map you display has been rendered into. The rendering and
displaying mechanisms are entirely separate, and the relationship
between what is rendered and what is visible on the screen is entirely
under the control of the programmer. The only common point of both
mechanisms is the BitMap structure, which is used for both rendering
and display.
Graphics Rendering
We shall deal with graphics rendering first, since rendering into a
bitmap is much simpler and more straightforward than displaying it.
There are two structures used to control the rendering process - the
BitMap (described above) and the "RastPort". The RastPort contains a
large amount of information which determines how rendering into the
BitMap (to which the RastPort must point) will be performed.
"Foreground", "Background" and "Outline" pen colours determine the
colours used to render lines, rectangles, text etc. and affect
area-filling operations. Line and area patterns are stored in the
RastPort, as are the characteristics (font, spacing etc.) of text
rendered. The Kernel routine InitRastPort is used to
initialise the RastPort's fields to useful default values.
The Kernel provides a large number of routines for graphics rendering,
most of which require the address of a RastPort. These include routines
to set the foreground, background and outline colours, and draw lines,
outline and filled polygons, filled rectangles, ellipses and circles
in the current foreground colour and line and area patterns. Also
provided are routines to draw text into the bitmap and move rectangular
blocks of pixels from one bit map to another.
Graphics Displaying
Graphics Modes and Colours
The programmer has a number of choices as to how a given bit map may
be displayed. He may select:
- low or high resolution (320 or 640 pixels per line respectively);
- non-interlaced or interlaced display (200 or 400 screen lines);
- one of several "special" modes, namely HAM (Hold and Modify), extra
half bright, genlock video and dual playfield. Each of these modes may
be applied to the whole screen or a selected portion of it.
If no special display mode is selected, the colour which will be
displayed on the screen for each pixel of the bit map is determined by
its colour number and the corresponding entry in the "colour palette",
an array which contains the actual red, green and blue intensities of
the screen colour which is displayed for each colour number. This
palette can have a maximum of 32 entries, which is fully utilized by
the low-resolution display of a 5 plane bit map. However, a
high-resolution display may only use a 4 plane bitmap.
Some of the special graphics modes can use 6 bit planes, for example HAM
and dual-playfield. However, these do not directly use the colour palette
to generate displays.
The following sections describe the three "levels" at which graphics may
be displayed - Kernel, Intuition and Copper. We shall begin with Kernel
graphics, since this knowledge provides the best basis for understanding
the rest.
Kernel Graphics
The appearance of the Amiga's screen is described by a group of data
structures which are created and manipulated by the programmer. These
structures are used by Kernel routines to create the displays desired
by the programmer.
Variables affecting the entire screen are stored in the "View" structure.
These are:
- the offset of the top left corner of the visible screen area from a
defined position, allowing the display as a whole to be easily moved;
- the addresses of the hardware copper lists for the display
(see Copper Graphics);
- flags describing various display parameters;
- a pointer to a ViewPort structure (see below).
The screen display created by use of the View structure is also termed
a "view".
The Amiga's screen can be subdivided into any number of horizontal
slices (at least one must be present) known as "viewports", each of which
is described by a ViewPort structure. This structure contains:
- the width and height of the viewport in pixels;
- the offset in pixels of the top left corner of the viewport from the
top left corner of the view;
- a pointer to a "RasInfo" structure (to be discussed below), which
contains information about the bit map to be displayed in this viewport;
- a pointer to a colour map. The colour map is a structure which contains
a palette and related information;
- flags indicating the graphics modes to be used in this viewport (as
described in Graphics Modes);
- pointers to user and system intermediate copper lists (see Copper
Graphics);
- a pointer to the next viewport in the view, which is zero of this is
the last.
Note that the viewports must not overlap, and a gap of one to three
screen lines, depending on the graphics modes used in each viewport,
must be left between them.
Each ViewPort points to a RasInfo structure, which contains the
following information:
- a pointer to a BitMap structure which describes the bit map to be
displayed in this viewport;
- the offset of the top left corner of the rectangular region displayed
in the viewport from the top left corner of the bit map. By changing
this offset you can easily display different portions of a bit map
too large to fit on the screen, for example to smoothly scroll a game
background across the screen.
- a pointer to another RasInfo structure, required when the dual
playfield graphics mode is used for this viewport.
In summary, then, the Kernel data structures needed to create a full
display are:
- View : describes the screen as a whole, points to first ViewPort;
- ViewPort : describes a horizontal slice of the screen;
- RasInfo : describes the region to be displayed;
- BitMap : describes the bit map containing the region to be displayed.
A typical display containing three viewports, the second of which uses
dual-playfield mode, would require the following structures to be set
up by the programmer:
( -> indicates "points to")
View
|
V
ViewPort -> ColorMap
| -> RasInfo -> BitMap
|
V
ViewPort -> ColorMap
| -> RasInfo -> BitMap
| |
| V
| RasInfo -> BitMap
|
V
ViewPort -> ColorMap
-> RasInfo -> BitMap
The View and ViewPort structures are initialised by the InitView
and InitVPort routines respectively. The routines
GetColorMap and FreeColorMap may be used to
allocate and deallocate a ColorMap structure.
Once these structures are correctly initialised and linked to each other,
Kernel routines are used to generate a display from them:
- MakeVPort generates an intermediate copper list (see Copper
Graphics) for a ViewPort from a View and ViewPort structure; this
should be performed for each ViewPort which is new or whose parameters
have changed in any way;
- MrgCop generates hardware copper lists (see Copper Graphics)
for a view, using the View structure to collect all the ViewPort
intermediate copper lists and merge them together; MrgCop must be
called whenever a change occurs in any viewport parameter.
- LoadView causes the copper to execute the copper list created
by MrgCop, thus displaying the view. LoadView is generally called
immediately after MrgCop.
As an example of the use of these routines, consider a view containing
two viewports. The first uses dual-playfield mode to scroll two bit maps
independently, the second switches back and forth slowly between two
separate pictures on different bit maps, each with its own BitMap
structure:
- initialise and link structures;
- call MakeVPort for each ViewPort;
Loop start:
- set offsets in both RasInfo structures for first viewport;
- call MakeVPort for first viewport
- if it's time to switch pictures in the second viewport, do:
- set BitMap pointer in RasInfo to point to desired BitMap
structure
- call MakeVPort for second viewport;
- call MrgCop and LoadView;
- go back to loop start.
Intuition Graphics
This sections explains the relationship between the Intuition user
interface and the Kernel graphics described above, and how to ensure
that your programs do not destroy this interface.
Intuition is a task which provides an interface between the user and
the operating system AmigaDOS, allowing the user to communicate his
desires to the programs using the interface via objects intended to
appear "concrete" to some extent. Intuition maintains two types of
screen memory through which the program and the user interact - the
"screen" and the "window".
Screens
A screen is a viewport which can be easily controlled by the user. If
a screen has a "title bar" along the top (rendered into the screen's bit
map by Intuition) the user can pull the screen down with the mouse -
i.e. alter the vertical offset of the viewport. If there is a screen
"behind" this one (Intuition maintains a depth-arranged list of screens)
a viewport is created for it which increases in size as the first screen
is pulled lower. On the right-hand side of the title bar are two gadgets
with which the user can push the screen to the back and bring it to the
front. If the title bar of a screen is not present the user cannot move
it.
To create a screen, a "NewScreen" structure must be initialised. It
contains information about both the viewport (e.g. graphics mode, height
and width) and bit map (e.g. height, width, depth) needed for a screen.
This structure is then used by the routine OpenScreen to
create (open) a screen. OpenScreen returns a pointer to a structure,
known as a Screen, which contains a complete ViewPort structure, a
pointer to a RastPort, a pointer to the first Window on the Screen
(see below) and various information used by Intuition to manage its
collection of Screens. The RastPort created by OpenScreen may be used
in the manner described in Graphics Rendering, with one important
caveat: since Intuition renders the Screen's title bar and menus into
the same bit map as that pointed to by the RastPort, you may overwrite
them by using the Screen's RastPort. This is quite safe, but
disconcerting to the user !
Windows
A window is a "virtual" piece of display memory which uses a rectangular
region of a Screen's bit map, but can be made to appear to the programmer
and user as though it has its own private screen memory. A window can
be set up such that when another window overlaps it, the obscured portion
is saved in an off-screen bit map and restored when that region of the
window is restored. windows (if permitted by the programmer) may be
moved, resized, depth-arranged and closed (removed), and are the most
powerful element of Intuition. Each window has associated with it a
"Layer" structure, which Intuition uses to ensure that all rendering
into a window is clipped at it boundaries, and at edges where overlaying
windows cover it. To create a window, a "NewWindow" structure must be
initialised to indicate which options, from the large number available,
the window is to have.
The routine OpenWindow is then called to create the window;
it returns the address of a "Window" structure, which contains
information about the status of the window and its environment, in
particular a pointer to the window's RastPort.
It is possible to create a window which covers an entire screen and yet
is invisible by specifying the BACKDROP and BORDERLESS flags when
creating the window. Although this might appear to have little point,
drawing to the RastPort of this window rather than that of the screen
has two advantages - any rendering outside the window (and thus the
screen) is clipped, which protects you from altering memory you haven't
allocated, and the title bar and menus cannot be overwritten.
What You Can Get Away With
It is quite safe to use most of the facilities of Kernel and Copper
graphics as long as several important points are borne in mind:
- Intuition uses a view for its display, which is accessible to the
programmer via the IntuitionBase data structure. If you alter the
viewport of your screen in such a way that a MakeVPort-MrgCop-LoadView
sequence is required to update the display, resist the temptation to
do this yourself with Intuition's view. If a screen is being moved by
the user while you are calling these routines the system will crash,
since Intuition is simultaneously doing the same to update the same
view. If you really must do this, call Forbid before and
Permit after these calls to lock out Intuition. It is far
better to use the Intuition routines MakeScreen,
RemakeDisplay and RethinkDisplay, which
perform the same operations in co-operation with Intuition;
- Rendering with a screen's RastPort is dangerous, since there is no
protection against overwriting the title bar, menus, windows or
off-screen memory. Unless there are no menus, windows or title bar
on the screen use a backdrop-borderless window instead;
- Don't make any assumptions about the position of your screen on the
Amiga's display or its visibility. This applies when using copper
list instructions, whose positional origin bears to relation to your
screen's viewport or even the view. Using the User Copper List
mechanism adjusts copper instructions to stay in position relative
to your screen.
Copper Graphics
What the Copper Does
The copper is a simple, fast microprocessor designed to control the
video display of the Amiga, although it can manipulate other system
hardware. It does this by modifying word-length "hardware registers",
which control the Amiga's custom chips, at times when the video beam
is at a given position on the display. These hardware registers are
mapped into the Amiga's address space, and may thus be read and written
directly by the programmer as well.
The copper has three instructions, each of which occupies two words.
They are:
- WAIT puts the copper into an inactive state until the specified beam
position is reached, whereupon the next instruction in the copper list
(see below) is executed;
- MOVE places a data word into a hardware register;
- SKIP jumps over the next instruction in the copper list if the video
beam is already past the specified position.
Note that since the address of the current copper instruction is
itself contained in a hardware register, the MOVE instruction can
perform a jump by writing to this register.
It is important to note that the beam origin point used by the WAIT
instruction is not the top left corner of a viewport or view, but the
point (probably off the screen) relative to which the view is positioned.
This makes it tedious to calculate beam positions, particularly when
dealing with Kernel or Intiuition displays.
Direct Copper Programming
The program executed by the copper is termed a "copper list", and
consists of a contiguous series of MOVE, WAIT and SKIP instructions in
memory.
There may be two copper lists - the "long frame" list which is executed
while the beam is visible and an optional "short frame" list which is
executed while the beam is in flyback (i.e. returning to the top of the
screen). The addresses of theses lists are stored in hardware registers,
and are automatically loaded into the copper instruction address
register at the appropriate time so that the same lists are executed for
each video frame.
Using a hardware copper list is very straightforward; you need to:
- allocate chip memory for your list;
- write your instructions;
- set the copper list hardware registers.
User Copper Programming
Although the copper can be programmed at the hardware level as described
so far, the user must take complete control of the display. To allow
copper lists to be integrated with existing Kernel or Intuition displays,
the data structures CopIns, UCopList and CopList
are provided. The copper actions specified using these structures are
made relative to a specified viewport rather than the whole display,
which allows them to be properly co-ordinated with Kernel or Intuition
graphics.
The CopIns data structure corresponds to a hardware copper instruction,
and is termed an "intermediate copper instruction". It contains the
following data:
- an opcode to mark this CopIns as a MOVE or WAIT instruction or the
end of a CopIns block (see below). SKIP instructions are not allowed;
- Vertical and Horizontal beam positions (for a WAIT) or a hardware
register address and data (for a MOVE).
The CopList structure points to a contiguous block of CopIns structures
(and specifies how many may be in the block), and to the next CopList
if it exists. The UCopList structure, which is pointed to by a ViewPort,
likewise points to a CopList and the next UCopList.
The structure of a complex user copper list may thus be:
ViewPort
|
V
UCopList -> CopList -> CopIns,CopIns,CopIns... (adjacent)
| |
| V
| CopList -> CopIns,CopIns,CopIns...
V
UCopList -> CopList -> CopIns,CopIns,CopIns...
Fortunately for the user, Kernel routines are provided to create and
manage these structures:
- CMOVE and CWAIT add a MOVE or WAIT instruction to a specified UCopList;
- CEND adds a WAIT instruction to a UCopList with a beam position which
cannot be attained, thus effectively "stopping" the copper.The user must
provide a UCopList structure, but CMOVE, CWAIT and CEND handle the
creation and manipulation of the CopList and UCopList structures. When
the intermediate copper list is no longer needed, it may be deallocated
with a call to the routine FreeVPortCopLists.
Once the user copper list is complete, MrgCop is used to created a
hardware copper list for the view from the intermediate copper lists of
each viewport, which include the user copper lists and those created by
MakeVPort. MrgCop creates a cprlist structure which points to a
hardware copper list. This list, which is pointed to by the View
structure, is used by LoadView to set the appropriate hardware registers.
Direct vs User Copper Programming
The major advantage of direct programming over user programming is of
speed and efficiency - the creation and deallocation of a user copper
list take time, as does the execution of MrgCop. Thus a game, where
speed is the major consideration, might use direct copper programming
even though more work is required of the programmer to set up the list.
On the other hand, if the copper list is to be executed within a Kernel
or Intuition display, the user programming approach must be used.
Appendices
Appendix A -
Basic Concepts
DATA STRUCTURE (or Structure): a group of data items which is known by a
collective name. The address of the structure is the same as the address
of its first field;
FIELD or ITEM: An element of a data structure;
POINTER: the address of another object, generally a data structure;
"POINTS TO" - if one structure points to another, one of its fields is
a pointer to that structure.
Appendix B
- Important Kernel Routines
(display only)
AllocMem: Allocates a block of memory whose size is specified in
bytes, returning the address of the block. The user specifies whether
FAST or CHIP memory is desired.
AllocRaster: As for AllocMem, except that the size of the bit
map (in pixels height and width) is given. The memory is allocated from
CHIP ram.
CloseScreen: Closes an Intuition screen, deallocating all
structures associated with the screen's viewport. Calls RemakeDisplay to
rebuild the Intuition display.
CloseWindow: Closes an Intuition window.
CopperListInit: Initialises a UCopList structure.
FreeColorMap: Deallocates a ColorMap structure and its
associated color table (viewport palette).
FreeCprList: Deallocates a cprlist structure and hardware
copper list (normally created by MrgCop).
FreeMem: Frees a block of memory whose size in bytes and starting
address is given.
FreeRaster: As for FreeMem, except that bit map dimensions are
given to specify the block size.
GetColorMap: Allocates and initialises a ColorMap structure
and color table, whose size is specified.
InitBitMap: Initialises a BitMap structure.
InitRastPort: Initialises a RastPort structure.
InitVPort: Initialises a ViewPort structure.
InitView: Initialises a View structure. Note that the dxOffset
and dyOffset fields are set to the DEFAULT Preferences values, not the
current ones.
LoadRGB4: Copies an array of colour values into a viewport's
color table, and then calls MakeVPort, MrgCop and LoadView to update the
colours on the display.
LoadView: Sets the hardware registers COP1LCx and COP2LCx (two
registers each) to the start of the hardware copper lists created by
MrgCop.
MakeScreen: Calls MakeVPort for the viewport of an Intuition
screen, ensuring that the call does not clash with Intuition's own
manipulation of the viewport.
MakeVPort: Creates an intermediate copper list from data in the
ViewPort structure and the ColorMap and RasInfo structures it points to.
The list contains instructions to set the colour, graphics mode and bit
plane hardware registers, among others. The address of the CopList
structure describing the list is placed in the dspIns pointer of the
ViewPort; if dspIns is not zero at the time MakeVPort is called (i.e.
there is already a dspIns list for this viewport) the copper list it
points to is deallocated.
MrgCop: Merges the intermediate copper lists of each viewport in
the specified view into one hardware list, adjusting the beam position
specified in each WAIT instruction so that it occurs at the correct
position relative to the viewport's desired position on the display.
MrgCop also creates a cprlist structure which points to the hardware
list. The address of the cprlist structure is stored in the LOFCprList
field of the View structure (MrgCop does not create a short form copper
list). As for MakeVPort, if LOFCprList is non-zero the current cprlist
and hardware list will be deallocated before the new list is created.
OpenScreen: Creates an Intuition screen. Allocates a Screen
structure (which contains a ViewPort structure), a RastPort, and all
other structures required by the viewport. Calls RemakeDisplay to
rebuild the Intuition Display.
OpenWindow: Creates an Intuition window on the specified
screen, allocating a Window structure, a RastPort and other structures
required for Intuition's management of the window.
RemakeDisplay: Rebuilds the entire Intuition display. Calls
MakeScreen for ALL screens, and then calls RethinkDisplay.
RethinkDisplay: Determines the position and size of each
Intuition screen's viewport on the display, updating the appropriate
fields in the ViewPort structure contained in each Screen structure.
Then calls MrgCop and LoadView to update the Intuition display
accordingly.
ScrollVPort: Moves the position of a bit map within a viewport
by altering the offset fields in the RasInfo pointed to by the ViewPort
by the x and y values specified, and then calling MakeVPort, MrgCop and
LoadView to update the display.
SetRGB4: As for LoadRGB4, except that only specified colour is
altered.
^^^^^^^^^^^^^^^^^^^^^^^^ END OF GFX_PROG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^